home *** CD-ROM | disk | FTP | other *** search
/ Aminet 3 / Aminet 3 - July 1994.iso / Aminet / util / misc / aterminfo.lha / comp_parse.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-12  |  16.3 KB  |  702 lines

  1.  
  2. /* This work is copyrighted. See COPYRIGHT.OLD & COPYRIGHT.NEW for   *
  3. *  details. If they are missing then this copy is in violation of    *
  4. *  the copyright conditions.                                        */
  5.  
  6. /*
  7.  *    comp_parse.c -- The high-level (ha!) parts of the compiler,
  8.  *            that is, the routines which drive the scanner,
  9.  *            etc.
  10.  *
  11.  */
  12.  
  13. #ifndef AMIGA
  14. #include <unistd.h>
  15. #endif
  16. #include <sys/types.h>
  17. #include <sys/stat.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <ctype.h>
  21. #include <string.h>
  22. #include "compiler.h"
  23. #include "term.h"
  24. #include "object.h"
  25.  
  26. extern int read_entry(char *, TERMINAL*);
  27. extern int must_swap();
  28.  
  29. char    *string_table;
  30. int    next_free;    /* next free character in string_table */
  31. int    table_size = 0; /* current string_table size */
  32. short    term_names;    /* string table offset - current terminal */
  33. int    part2 = 0;    /* set to allow old compiled defns to be used */
  34. int    complete = 0;    /* 1 if entry done with no forward uses */
  35.  
  36. typedef struct use_item
  37. {
  38.     long    offset;
  39.     struct use_item    *fptr, *bptr;
  40. } USE_ITEM;
  41.  
  42. struct use_header
  43. {
  44.     USE_ITEM *head, *tail;
  45. };
  46.  
  47. struct use_header    use_list = {NULL, NULL};
  48. int            use_count = 0;
  49.  
  50. /*
  51.  *  The use_list is a doubly-linked list with NULLs terminating the lists:
  52.  *
  53.  *       use_item    use_item    use_item
  54.  *      ---------   ---------   ---------
  55.  *      |       |   |       |   |       |   offset
  56.  *        |-------|   |-------|   |-------|
  57.  *      |   ----+-->|   ----+-->|  NULL |   fptr
  58.  *      |-------|   |-------|   |-------|
  59.  *      |  NULL |<--+----   |<--+----   |   bptr
  60.  *      ---------   ---------   ---------
  61.  *      ^                       ^
  62.  *      |  ------------------   |
  63.  *      |  |       |        |   |
  64.  *      +--+----   |    ----+---+
  65.  *         |       |        |
  66.  *         ------------------
  67.  *           head     tail
  68.  *              use_list
  69.  *
  70.  */
  71.  
  72. static int do_entry(USE_ITEM *);
  73. static void dequeue(USE_ITEM *);
  74. static void init_structure(char *, short *, short *);
  75. static int save_str(char *);
  76. static int handle_use(USE_ITEM *, long, char *, short *, short *);
  77. static void dump_structure(short, char *, short *, short *);
  78. static void check_name(char *);
  79. static int write_object(FILE *, short, char *, short *, short *);
  80.  
  81. /*
  82.  *    compile()
  83.  *
  84.  *    Main loop of the compiler.
  85.  *
  86.  *    get_token()
  87.  *    if curr_token != NAMES
  88.  *        err_abort()
  89.  *    while (not at end of file)
  90.  *        do an entry
  91.  *
  92.  */
  93.  
  94. void compile()
  95. {
  96. char        line[1024];
  97. int        token_type;
  98. USE_ITEM    *ptr;
  99. int        old_use_count;
  100.  
  101.     token_type = get_token();
  102.  
  103.     if (token_type != NAMES)
  104.         err_abort("File does not start with terminal names in column one");
  105.  
  106.     while (token_type != EOF)
  107.         token_type = do_entry(NULL);
  108.  
  109.     DEBUG0(2, "Starting handling of forward USE's\n");
  110.  
  111.     for (part2=0; part2<2; part2++) {
  112.         old_use_count = -1;
  113.         DEBUG(2, "\n\nPART %d\n\n", part2);
  114.         while (use_list.head != NULL  &&  old_use_count != use_count) {
  115.         old_use_count = use_count;
  116.         for (ptr = use_list.tail; ptr != NULL; ptr = ptr->bptr) {
  117.             fseek(curr_fh, ptr->offset, 0);
  118.             reset_input();
  119.             if ((token_type = get_token()) != NAMES)
  120.             syserr_abort("Token after a seek not NAMES");
  121.             (void) do_entry(ptr);
  122.             if (complete)
  123.             dequeue(ptr);
  124.         }
  125.  
  126.         for (ptr = use_list.head; ptr != NULL; ptr = ptr->fptr) {
  127.             fseek(curr_fh, ptr->offset, 0);
  128.             reset_input();
  129.             if ((token_type = get_token()) != NAMES)
  130.             syserr_abort("Token after a seek not NAMES");
  131.             (void) do_entry(ptr);
  132.             if (complete)
  133.             dequeue(ptr);
  134.         }
  135.  
  136.         DEBUG0(2, "Finished a pass through enqueued forward USE's\n");
  137.         }
  138.     }
  139.  
  140.     if (use_list.head != NULL) {
  141.         fprintf(stderr, "\nError in following up use-links.  Either there is\n");
  142.         fprintf(stderr, "a loop in the links or they reference non-existant\n");
  143.         fprintf(stderr, "terminals.  The following is a list of the entries\n");
  144.         fprintf(stderr, "involved:\n\n");
  145.  
  146.         for (ptr = use_list.head; ptr != NULL; ptr = ptr->fptr) {
  147.         fseek(curr_fh, ptr->offset, 0);
  148.         fgets(line, 1024, curr_fh);
  149.         fprintf(stderr, "%s", line);
  150.         }
  151.  
  152.         exit(1);
  153.     }
  154. }
  155.  
  156. void dump_list(str)
  157. char *str;
  158. {
  159.     USE_ITEM *ptr;
  160.     char line[512];
  161.  
  162.     fprintf(stderr, "dump_list %s\n", str);
  163.     for (ptr = use_list.head; ptr != NULL; ptr = ptr->fptr) {
  164.         fseek(curr_fh, ptr->offset, 0);
  165.         fgets(line, 1024, curr_fh);
  166.         fprintf(stderr, "ptr %x off %d bptr %x fptr %x str %s",
  167.                          (int) ptr,
  168.                  ptr->offset,
  169.                  (int) ptr->bptr,
  170.                  (int) ptr->fptr,
  171.                  line);
  172.     }
  173.     fprintf(stderr, "\n");
  174. }
  175.  
  176. /*
  177.  *    int
  178.  *    do_entry(item_ptr)
  179.  *
  180.  *    Compile one entry.  During the first pass, item_ptr is NULL.  In pass
  181.  *    two, item_ptr points to the current entry in the use_list.
  182.  *
  183.  *    found-forward-use = FALSE
  184.  *    re-initialise internal arrays
  185.  *    save names in string_table
  186.  *    get_token()
  187.  *    while (not EOF and not NAMES)
  188.  *        if found-forward-use
  189.  *        do nothing
  190.  *        else if 'use'
  191.  *        if handle_use() < 0
  192.  *            found-forward-use = TRUE
  193.  *          else
  194.  *            check for existance and type-correctness
  195.  *            enter cap into structure
  196.  *            if STRING
  197.  *                save string in string_table
  198.  *        get_token()
  199.  *      if ! found-forward-use
  200.  *        clear CANCELS out of the structure
  201.  *        dump compiled entry into filesystem
  202.  *
  203.  */
  204.  
  205. int
  206. do_entry(USE_ITEM *item_ptr)
  207. {
  208. long            entry_offset;
  209. int            i;
  210. int            token_type;
  211. struct name_table_entry    *entry_ptr;
  212. int            found_forward_use = FALSE;
  213. char            Booleans[BOOLCOUNT];
  214. short            Numbers[NUMCOUNT], Strings[STRCOUNT];
  215.  
  216.     init_structure(Booleans, Numbers, Strings);
  217.     complete = 0;
  218.     term_names = save_str(curr_token.tk_name);
  219.     DEBUG(2, "Starting '%s'\n", curr_token.tk_name);
  220.     entry_offset = curr_file_pos;
  221.  
  222.     for (token_type = get_token();
  223.         token_type != EOF  &&  token_type != NAMES;
  224.         token_type = get_token())
  225.     {
  226.         if (found_forward_use)
  227.         /* do nothing */ ;
  228.         else if (strcmp(curr_token.tk_name, "use") == 0) {
  229.         if (handle_use(item_ptr, entry_offset,
  230.                     Booleans, Numbers, Strings) < 0)
  231.             found_forward_use = TRUE;
  232.         } else {
  233.         entry_ptr = find_entry(curr_token.tk_name);
  234.  
  235.         if (entry_ptr == NOTFOUND) {
  236.             warning("Unknown Capability - '%s'",
  237.                                                           curr_token.tk_name);
  238.             continue;
  239.         }
  240.  
  241.  
  242.         if (token_type != CANCEL &&  entry_ptr->nte_type != token_type)
  243.             warning("Wrong type used for capability '%s'",
  244.               curr_token.tk_name);
  245.         switch (token_type) {
  246.             case CANCEL:
  247.             switch (entry_ptr->nte_type) {
  248.                 case BOOLEAN:
  249.                 Booleans[entry_ptr->nte_index] = -2;
  250.                 break;
  251.  
  252.                 case NUMBER:
  253.                 Numbers[entry_ptr->nte_index] = -2;
  254.                 break;
  255.  
  256.                 case STRING:
  257.                 Strings[entry_ptr->nte_index] = -2;
  258.                 break;
  259.             }
  260.             break;
  261.  
  262.             case BOOLEAN:
  263.             Booleans[entry_ptr->nte_index] = TRUE;
  264.             break;
  265.  
  266.             case NUMBER:
  267.             Numbers[entry_ptr->nte_index] =
  268.                                                       curr_token.tk_valnumber;
  269.             break;
  270.  
  271.             case STRING:
  272.             Strings[entry_ptr->nte_index] =
  273.                                             save_str(curr_token.tk_valstring);
  274.             break;
  275.  
  276.             default:
  277.             warning("Unknown token type");
  278.             panic_mode(',');
  279.             continue;
  280.         }
  281.         } /* end else cur_token.name != "use" */
  282.  
  283.     } /* endwhile (not EOF and not NAMES) */
  284.  
  285.     if (found_forward_use)
  286.         return(token_type);
  287.  
  288.     for (i=0; i < BOOLCOUNT; i++) {
  289.         if (Booleans[i] == -2)
  290.         Booleans[i] = FALSE;
  291.     }
  292.  
  293.     for (i=0; i < NUMCOUNT; i++) {
  294.         if (Numbers[i] == -2)
  295.         Numbers[i] = -1;
  296.     }
  297.  
  298.     for (i=0; i < STRCOUNT; i++) {
  299.         if (Strings[i] == -2)
  300.         Strings[i] = -1;
  301.     }
  302.  
  303.     dump_structure(term_names, Booleans, Numbers, Strings);
  304.  
  305.     complete = 1;
  306.     return(token_type);
  307. }
  308.  
  309.  
  310. /*
  311.  *    enqueue(offset)
  312.  *
  313.  *      Put a record of the given offset onto the use-list.
  314.  *
  315.  */
  316.  
  317. void enqueue(long offset)
  318. {
  319. USE_ITEM *item;
  320.  
  321.     item = (USE_ITEM *) malloc(sizeof(USE_ITEM));
  322.  
  323.     if (item == NULL)
  324.         syserr_abort("Not enough memory for use_list element");
  325.  
  326.     item->offset = offset;
  327.  
  328.     if (use_list.head != NULL) {
  329.         item->bptr = use_list.tail;
  330.         use_list.tail->fptr = item;
  331.         item->fptr = NULL;
  332.         use_list.tail = item;
  333.     } else {
  334.         use_list.tail = use_list.head = item;
  335.         item->fptr = item->bptr = NULL;
  336.     }
  337.  
  338.     use_count ++;
  339. }
  340.  
  341. /*
  342.  *    dequeue(ptr)
  343.  *
  344.  *    remove the pointed-to item from the use_list
  345.  *
  346.  */
  347.  
  348. void dequeue(USE_ITEM *ptr)
  349. {
  350.     if (ptr->fptr == NULL)
  351.         use_list.tail = ptr->bptr;
  352.     else
  353.         (ptr->fptr)->bptr = ptr->bptr;
  354.  
  355.     if (ptr->bptr == NULL)
  356.         use_list.head = ptr->fptr;
  357.     else
  358.         (ptr->bptr)->fptr = ptr->fptr;
  359.  
  360.     use_count --;
  361. }
  362.  
  363. /*
  364.  *    dump_structure()
  365.  *
  366.  *    Save the compiled version of a description in the filesystem.
  367.  *
  368.  *    make a copy of the name-list
  369.  *    break it up into first-name and all-but-last-name
  370.  *    creat(first-name)
  371.  *    write object information to first-name
  372.  *    close(first-name)
  373.  *      for each name in all-but-last-name
  374.  *        link to first-name
  375.  *
  376.  */
  377.  
  378. void dump_structure(short term_names, char *Booleans, short *Numbers, short *Strings)
  379. {
  380. struct stat    statbuf;
  381. FILE        *fp;
  382. char        name_list[1024];
  383. char        *first_name, *other_names;
  384. char        *ptr;
  385. char        filename[50];
  386. char        linkname[50];
  387. extern char check_only;
  388.  
  389.     strcpy(name_list, term_names + string_table);
  390.     DEBUG(7, "Name list = '%s'\n", name_list);
  391.  
  392.     first_name = name_list;
  393.  
  394.     ptr = &name_list[strlen(name_list) - 1];
  395.     other_names = ptr + 1;
  396.  
  397.     while (ptr > name_list  &&  *ptr != '|')
  398.         ptr--;
  399.  
  400.     if (ptr != name_list) {
  401.         *ptr = '\0';
  402.  
  403.         for (ptr = name_list; *ptr != '\0'  &&  *ptr != '|'; ptr++)
  404.         ;
  405.  
  406.         if (*ptr == '\0')
  407.         other_names = ptr;
  408.         else {
  409.         *ptr = '\0';
  410.         other_names = ptr + 1;
  411.         }
  412.     }
  413.  
  414.     if (check_only) {
  415.         DEBUG(1, "Checked %s\n", first_name);
  416.         return;
  417.     }
  418.  
  419.     DEBUG(7, "First name = '%s'\n", first_name);
  420.     DEBUG(7, "Other names = '%s'\n", other_names);
  421.  
  422.     if (strlen(first_name) > 100)
  423.         warning("'%s': terminal name too long.", first_name);
  424.  
  425.     check_name(first_name);
  426.  
  427.     sprintf(filename, "%c/%s", first_name[0], first_name);
  428.  
  429.     if (stat(filename, &statbuf) >= 0  &&  statbuf.st_mtime >= start_time) {
  430.         warning("'%s' defined in more than one entry.", first_name);
  431.         fprintf(stderr, "Entry being used is '%s'.\n",
  432.                 (unsigned) term_names + string_table);
  433.     }
  434.  
  435.     unlink(filename);
  436.     fp = fopen(filename, "w");
  437.     if (fp == NULL) {
  438.         perror(filename);
  439.         syserr_abort("Can't open %s/%s\n", destination, filename);
  440.     }
  441.     DEBUG(1, "Created %s\n", filename);
  442.  
  443.     if (write_object(fp, term_names, Booleans, Numbers, Strings) < 0) {
  444.         syserr_abort("Error in writing %s/%s", destination, filename);
  445.     }
  446.     fclose(fp);
  447.  
  448.     while (*other_names != '\0') {
  449.         ptr = other_names++;
  450.         while (*other_names != '|'  &&  *other_names != '\0')
  451.         other_names++;
  452.  
  453.         if (*other_names != '\0')
  454.         *(other_names++) = '\0';
  455.  
  456.         if (strlen(ptr) > 100) {
  457.         warning("'%s': terminal name too long.", ptr);
  458.         continue;
  459.         }
  460.  
  461.         sprintf(linkname, "%c/%s", ptr[0], ptr);
  462.  
  463.         if (strcmp(filename, linkname) == 0) {
  464.         warning("Terminal name '%s' synonym for itself", first_name);
  465.         }
  466.         else if (stat(linkname, &statbuf) >= 0  &&
  467.                         statbuf.st_mtime >= start_time)
  468.         {
  469.         warning("'%s' defined in more than one entry.", ptr);
  470.         fprintf(stderr, "Entry being used is '%s'.\n",
  471.                 (unsigned) term_names + string_table);
  472.         } else {
  473.         unlink(linkname);
  474. #ifndef AMIGA
  475.         if (link(filename, linkname) < 0)
  476.             syserr_abort("Can't link %s to %s", filename, linkname);
  477. #endif
  478.         DEBUG(1, "Linked %s\n", linkname);
  479.         }
  480.     }
  481. }
  482.  
  483.  
  484. /*
  485.  *    int
  486.  *    write_object(fp, term_names, Booleans, Numbers, Strings)
  487.  *
  488.  *    Write out the compiled entry to the given file.
  489.  *    Return 0 if OK or -1 if not.
  490.  *
  491.  */
  492.  
  493. #define swap(x)        (((x >> 8) & 0377) + 256 * (x & 0377))
  494.  
  495. #define might_swap(x)    (must_swap()  ?  swap(x)  :  (x))
  496.  
  497.  
  498. int
  499. write_object(FILE *fp, short term_names,
  500.          char *Booleans, short *Numbers, short *Strings)
  501. {
  502. struct header    header;
  503. char        *namelist;
  504. short        namelen;
  505. char        zero = '\0';
  506. int        i;
  507.  
  508.     namelist = term_names + string_table;
  509.     namelen = strlen(namelist) + 1;
  510.  
  511.     if (must_swap()) {
  512.         header.magic = swap(MAGIC);
  513.         header.name_size = swap(namelen);
  514.         header.bool_count = swap(BOOLCOUNT);
  515.         header.num_count = swap(NUMCOUNT);
  516.         header.str_count = swap(STRCOUNT);
  517.         header.str_size = swap(next_free);
  518.     } else {
  519.         header.magic = MAGIC;
  520.         header.name_size = namelen;
  521.         header.bool_count = BOOLCOUNT;
  522.         header.num_count = NUMCOUNT;
  523.         header.str_count = STRCOUNT;
  524.         header.str_size = next_free;
  525.     }
  526.  
  527.     if (fwrite(&header, sizeof(header), 1, fp) != 1
  528.         ||  fwrite(namelist, sizeof(char), namelen, fp) != namelen
  529.         ||  fwrite(Booleans, sizeof(char), BOOLCOUNT, fp) != BOOLCOUNT)
  530.         return(-1);
  531.  
  532.     if ((namelen+BOOLCOUNT) % 2 != 0  &&  fwrite(&zero, sizeof(char), 1, fp) != 1)
  533.         return(-1);
  534.  
  535.     if (must_swap()) {
  536.         for (i=0; i < NUMCOUNT; i++)
  537.         Numbers[i] = swap(Numbers[i]);
  538.         for (i=0; i < STRCOUNT; i++)
  539.         Strings[i] = swap(Strings[i]);
  540.     }
  541.  
  542.     if (fwrite(Numbers, sizeof(short), NUMCOUNT, fp) != NUMCOUNT
  543.            ||  fwrite(Strings, sizeof(short), STRCOUNT, fp) != STRCOUNT
  544.            ||  fwrite(string_table, sizeof(char), next_free, fp)
  545.                                   != next_free)
  546.         return(-1);
  547.  
  548.         return(1);
  549. }
  550.  
  551.  
  552. /*
  553.  *    check_name(name)
  554.  *
  555.  *    Generate an error message if given name does not begin with a
  556.  *    digit or lower-case letter.
  557.  *
  558.  */
  559.  
  560. void check_name(char *name)
  561. {
  562.     if (! isdigit(name[0])  &&  ! islower(name[0])) {
  563.         fprintf(stderr, "compile: Line %d: Illegal terminal name - '%s'\n",
  564.                                 curr_line, name);
  565.         fprintf(stderr,
  566.             "Terminal names must start with lowercase or digit\n");
  567.         exit(1);
  568.     }
  569. }
  570.  
  571.  
  572. /*
  573.  *    int
  574.  *    save_str(string)
  575.  *
  576.  *    copy string into next free part of string_table, doing a realloc()
  577.  *    if necessary.  return offset of beginning of string from start of
  578.  *    string_table.
  579.  *
  580.  */
  581.  
  582. int save_str(char *string)
  583. {
  584. int    old_next_free = next_free;
  585.  
  586.     if (table_size == 0) {
  587.         if ((string_table = malloc(1024)) == NULL)
  588.         syserr_abort("Out of memory");
  589.         table_size = 1024;
  590.         DEBUG(5, "Made initial string table allocation.  Size is %d\n",
  591.                                     table_size);
  592.     }
  593.  
  594.     while (table_size < next_free + strlen(string)) {
  595.         if ((string_table = realloc(string_table, table_size + 1024))
  596.                                     == NULL)
  597.         syserr_abort("Out of memory");
  598.         table_size += 1024;
  599.         DEBUG(5, "Extended string table.  Size now %d\n", table_size);
  600.     }
  601.  
  602.     strcpy(&string_table[next_free], string);
  603.     DEBUG(7, "Saved string '%s' ", string);
  604.     DEBUG(7, "at location %d\n", next_free);
  605.     next_free += strlen(string) + 1;
  606.  
  607.     return(old_next_free);
  608. }
  609.  
  610.  
  611. /*
  612.  *    init_structure(Booleans, Numbers, Strings)
  613.  *
  614.  *    Initialise the given arrays
  615.  *    Reset the next_free counter to zero.
  616.  *
  617.  */
  618.  
  619. void init_structure(char *Booleans, short *Numbers, short *Strings)
  620. {
  621. int    i;
  622.  
  623.     for (i=0; i < BOOLCOUNT; i++)
  624.         Booleans[i] = FALSE;
  625.  
  626.     for (i=0; i < NUMCOUNT; i++)
  627.         Numbers[i] = -1;
  628.  
  629.     for (i=0; i < STRCOUNT; i++)
  630.         Strings[i] = -1;
  631.  
  632.     next_free = 0;
  633. }
  634.  
  635.  
  636. /*
  637. **    int
  638. **    handle_use(item_ptr, entry_offset, Booleans, Numbers, Strings)
  639. **
  640. **    Merge the compiled file whose name is in cur_token.valstring
  641. **    with the current entry.
  642. **
  643. **        if it's a forward use-link
  644. **                if item_ptr == NULL
  645. **                queue it up for later handling
  646. **                else
  647. **                ignore it (we're already going through the queue)
  648. **            else it's a backward use-link
  649. **                read in the object file for that terminal
  650. **                merge contents with current structure
  651. **
  652. **    Returned value is 0 if it was a backward link and we
  653. **    successfully read it in, -1 if a forward link.
  654. */
  655.  
  656. int handle_use(USE_ITEM *item_ptr, long entry_offset,
  657.                char *Booleans, short *Numbers, short *Strings)
  658. {
  659. struct term    use_term;
  660. struct stat    statbuf;
  661. char        filename[50];
  662. int             i;
  663.  
  664.     check_name(curr_token.tk_valstring);
  665.  
  666.     sprintf(filename, "%c/%s", curr_token.tk_valstring[0],
  667.                                                      curr_token.tk_valstring);
  668.  
  669.     if (stat(filename, &statbuf) < 0  ||  (part2==0 && statbuf.st_mtime < start_time)) {
  670.         DEBUG(2, "Forward USE to %s", curr_token.tk_valstring);
  671.  
  672.          if (item_ptr == NULL) {
  673.          DEBUG0(2, " (enqueued)\n");
  674.          enqueue(entry_offset);
  675.          } else
  676.          DEBUG0(2, " (skipped)\n");
  677.  
  678.         return(-1);
  679.     } else {
  680.         DEBUG(2, "Backward USE to %s\n", curr_token.tk_valstring);
  681.         if (read_entry(filename, &use_term) < 0)
  682.         syserr_abort("Error in re-reading compiled file %s", filename);
  683.  
  684.         for (i=0; i < BOOLCOUNT; i++) {
  685.         if (Booleans[i] == FALSE  &&  use_term.Booleans[i] == TRUE)
  686.             Booleans[i] = TRUE;
  687.         }
  688.  
  689.         for (i=0; i < NUMCOUNT; i++) {
  690.         if (Numbers[i] == -1  &&  use_term.Numbers[i] != -1)
  691.             Numbers[i] = use_term.Numbers[i];
  692.         }
  693.  
  694.         for (i=0; i < STRCOUNT; i++) {
  695.         if (Strings[i] == -1  &&  use_term.Strings[i] != (char *) 0)
  696.             Strings[i] = save_str(use_term.Strings[i]);
  697.         }
  698.  
  699.     }
  700.         return(1);
  701. }
  702.